---
id: dbcontext-code-first
title: 9.20 模型生成数据库
sidebar_label: 9.20 模型生成数据库 (Code First)
---

import useBaseUrl from "@docusaurus/useBaseUrl";

:::tip 开始之前

`Fur.Web.Entry` 层需要安装 `Microsoft.EntityFrameworkCore.Tools` 包。

:::

## 9.20.1 数据库开发方式

`Furion` 提供两种主要方法来 **保持实体模型和数据库架构同步**。

至于我们应该选用哪个方法，请确定你是希望以实体模型为准还是以数据库为准：

- 如果希望 **以实体模型为准**，请使用正向工程（Code First）。 对实体模型进行更改时，此方法会以增量方式将相应架构更改应用到数据库，以使数据库保持与实体模型兼容。

- 如果希望 **以数据库架构为准**，请使用反向工程（Database First）。 使用此方法，可通过将数据库架构反向工程到实体模型来生成相应的实体类型。

本章节是 **正向工程（Code First）** 的相关内容。

## 9.20.2 操作指南

:::important 特别注意

`Furion` 框架默认数据迁移的程序集为：`Furion.Database.Migrations`，所以如果您改了程序集名称或通过 `Nuget` 方式安装的 `Furion` 包，则需要配置迁移程序集名称：

```cs {4}
services.AddDatabaseAccessor(options =>
{
    options.AddDbPool<FurionDbContext>(DbProvider.Sqlite);
}, "存放迁移文件的项目名称");
```

另外，如果应用中配置了多个数据库上下文，那么所有的 `迁移命令` 都需要指定 `-Context 数据库上下文名称` 参数。如：

```shell
Add-Migration v1.0.0 -Context FurionDbContext
```

:::

### 9.20.2.1 创建实体模型 `Person`

```cs {1,6,8}
using Furion.DatabaseAccessor;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Furion.Core
{
    public class Person : Entity
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public Person()
        {
            CreatedTime = DateTime.Now;
            IsDeleted = false;
        }

        /// <summary>
        /// 姓名
        /// </summary>
        [MaxLength(32)]
        public string Name { get; set; }

        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }

        /// <summary>
        /// 住址
        /// </summary>
        public string Address { get; set; }
    }
}
```

:::important 实体约定

所有数据库实体必须直接或间接继承 `IEntity` 接口。

:::

### 9.20.2.2 打开 `程序包管理控制台`

<img src={useBaseUrl("img/dbfirst1.png")} />

### 9.20.2.3 切换默认项目

将 `程序包管理控制台` 默认项目设置为 `Furion.Database.Migrations`

<img src={useBaseUrl("img/codefirst1.png")} />

### 9.20.2.4 创建模型版本

```shell
Add-Migration v1.0.0
```

:::note 特别说明

**v1.0.0** 是此处数据库更改的版本号，可以写任何字符串，但推荐写版本号，每次 **+1**。

:::

最终命令如下：

```shell
PM> Add-Migration v1.0.0
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Model.Validation[10400]
      Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data, this mode should only be enabled during development.
Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 5.0.0-rc.1.20451.13 initialized 'FurionDbContext' using provider 'Microsoft.EntityFrameworkCore.Sqlite' with options: SensitiveDataLoggingEnabled DetailedErrorsEnabled MaxPoolSize=100 MigrationsAssembly=Furion.Database.Migrations
To undo this action, use Remove-Migration.
PM>
```

生成成功后，`Furion.Database.Migrations` 项目下会新增 `Migrations` 文件夹（如果没有），同时本次的架构生成文件，如：

<img src={useBaseUrl("img/codefirst3.png")} />

### 9.20.2.5 更新到数据库

```shell
Update-Database
```

执行该命令后，数据库就会自动根据模型生成对应的表。

:::tip 小知识

如果 `Update-Database` 后面带字符串参数，则会自动还原数据库到指定版本，如：

```shell
Update-Database v0.0.3
```

将数据库还原到 `v0.0.3` 版本

:::

## 9.20.3 更新模型

如果模型改变了，重复上面操作即可，如：

```shell
Add-Migration v1.0.1
```

```shell
Update-Database
```

## 9.20.4 导出 `Sql`

有些时候，我们没有直接更新数据库的权限，或者怕出问题，我们都会先生成 `Sql` 看看，这时候只需要通过 `Script-Migration` 导出即可，如：

```shell
Script-Migration
```

<img src={useBaseUrl("img/codefirst2.png")} />

## 9.20.5 `VS Code/Rider/任何IDE/操作系统` 方式

### 9.20.5.1 安装 `dotnet ef`

```shell
dotnet tool install --global dotnet-ef --version 5.0.12
```

### 9.20.5.2 `cd` 目录

通过 `VS Code` 打开 `.sln` 所在的目录，如：`framework`。

之后进入 `Furion.Database.Migrations` 目录

```shell
cd Furion.Database.Migrations
```

### 9.20.5.3 执行命令

```shell
dotnet ef migrations add v1.0.0 -s "../Furion.Web.Entry"
```

```shell
dotnet ef database update -s "../Furion.Web.Entry"
```

## 9.20.6 应用启动时自动生成数据库

`Furion` 框架建议大家使用命令方式操作数据库，**完全不推荐自动化生成数据库**，但是有些特殊情况下，有这个必要，故将此功能写出：

### 9.20.6.1 对已经生成 `Migrations` 文件情况

如果已经生成 `Migrations` 文件，那么可以直接在 `Startup.cs` 代码中实现程序启动时自动执行 `update-database` 命令，如：

```cs {1,4,6,8-9}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // 判断开发环境！！！必须！！！！
    if (env.IsDevelopment())
    {
        Scoped.Create((_, scope) =>
        {
            var context = scope.ServiceProvider.GetRequiredService<FurionDbContext>();
            context.Database.Migrate();
        });
    }

    // 其他代码
}
```

### 9.20.6.2 如果没有生成过 `Migrations` 文件情况

```cs {1,4,6,8-9}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // 判断开发环境！！！必须！！！！
    if (env.IsDevelopment())
    {
        Scoped.Create((_, scope) =>
        {
            var context = scope.ServiceProvider.GetRequiredService<FurionDbContext>();
            context.Database.EnsureCreated();
        });
    }

    // 其他代码
}
```

**如果需要在创建数据库之前先删除旧的，可先调用 `context.Database.EnsureDeleted();` 代码。慎重！！！！！！！！！！！！**

## 9.20.7 反馈与建议

:::note 与我们交流

给 Furion 提 [Issue](https://gitee.com/dotnetchina/Furion/issues/new?issue)。

:::

---

:::note 了解更多

想了解更多 `正向工厂` 知识可查阅 [EF Core - 管理数据库架构](https://docs.microsoft.com/zh-cn/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli) 章节。

:::
